
#include <cffformat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


extern FILE* yyin;
cffNode* global_ast_node;
int yyparse ();

#if CC_WIN32
extern int yylineno = 0;
#else
extern int yylineno;
#endif

cffAttribute::cffAttribute ()
	: _valid (false)
  , _key(std::string(""))
  , _value(std::string(""))
{
}

cffAttribute::cffAttribute (const char* value)
  : _value (value)
  , _key (std::string(""))
	, _valid (true)
{
  _value = strdup(value);
}


cffAttribute::cffAttribute (const char* key, const char* value)
  : _key (std::string(key))
  , _value (std::string(value))
	, _valid (true)
{
}


bool cffAttribute::IsValid () const
{
	return _valid;
}

int cffAttribute::AsInt () const
{
  return atoi (_value.c_str());
}

float cffAttribute::AsFloat () const
{
  return atof (_value.c_str());
}

void cffAttribute::Debug ()
{
  printf ("%s=\"%s\" ", _key.c_str(), _value.c_str());
}





cffAttributes::cffAttributes ()
{
}

void cffAttributes::SetAttribute (const char* key, const char* value)
{
	_namedAttributes[key] = cffAttribute (key, value);
}

void cffAttributes::AddAttribute (const char* attribute)
{
	char buf [8];
  sprintf (buf, "%lu", _attributes.size ());
	_attributes.push_back(cffAttribute (buf, attribute));
}


bool cffAttributes::HasAttribute(unsigned idx) const
{
	return idx < _attributes.size();
}

bool cffAttributes::HasNamedAttribute(const char *name) const
{
	std::map<std::string, cffAttribute>::const_iterator it = _namedAttributes.find(name);
	return it != _namedAttributes.end();
}

const cffAttribute& cffAttributes::GetAttribute(unsigned idx) const
{
	return _attributes[idx];
}

const cffAttribute& cffAttributes::GetNamedAttribute(const char* name) const
{

	std::map<std::string, cffAttribute>::const_iterator it = _namedAttributes.find(name);
	if (it == _namedAttributes.end ())
	{
		return _null;
	}

	return it->second;
}

void cffAttributes::Debug ()
{
	printf ("<");
	for (unsigned i=0; i<_attributes.size (); ++i)
		{
			_attributes[i].Debug ();
		}
	std::map<std::string, cffAttribute>::iterator it;
	for (it = _namedAttributes.begin (); it != _namedAttributes.end (); ++it)
		{
			it->second.Debug ();
		}
	printf (">");
}

cffNode::cffNode ()
	: _parent (0)
	, _name ("")
{
	CE_OBJECT_CONSTR
	_nodes.clear();
}

cffNode::~cffNode ()
{
	std::list<cffNode*>::iterator it;
	for (it = _nodes.begin (); it != _nodes.end (); ++it)
		{
			cffNode* node = *it;
			node->Release ();
		}
	_nodes.clear ();
}


void cffNode::SetName (const char* name)
{
	if (!name)
		{
			_name = "";
		}
	else
		{
			_name = name;
		}
}

const char* cffNode::GetName () const
{
	return _name.c_str ();
}

cePtr<cffNode> cffNode::GetParent () const
{
	return _parent;
}

unsigned cffNode::GetNumberOfNodes (const char *name) const
{
	if (!name)
		{
			return _nodes.size ();
		}

	unsigned count = 0;
	std::string sName (name);
	std::list<cffNode*>::const_iterator it;
	unsigned i=0;
	for (it = _nodes.begin (); it !=_nodes.end (); ++it)
		{
			if ((*it)->_name == sName)
				{
					count++;
				}
		}
	return count;
}

cffNode* cffNode::GetNode (unsigned idx)
{
	std::list<cffNode*>::iterator it;
	unsigned i=0;
	for (it = _nodes.begin (); it !=_nodes.end (); ++it, ++i)
		{
			if (i == idx)
				{
					return *it;
				}
		}
	return 0;
}
cffNode* cffNode::GetFirstNode()
{
	if (_nodes.size() == 0)
		{
			return 0;
		}

	return *(_nodes.begin());
}

cffNode* cffNode::GetNode (const char* name, unsigned idx)
{
	std::string sName (name);
	std::list<cffNode*>::iterator it;
	unsigned i=0;
	for (it = _nodes.begin (); it !=_nodes.end (); ++it)
		{
			if ((*it)->_name == sName)
				{
					if (i == idx)
						{
							return *it;
						}
					++i;
				}
		}
	return 0;
}

cffAttributes& cffNode::GetAttributes ()
{
	return _attributes;
}

const cffAttributes& cffNode::GetAttributes () const
{
	return _attributes;
}

bool cffNode::HasAttribute (unsigned idx) const
{
	return _attributes.HasAttribute(idx);
}


bool cffNode::HasNamedAttribute (const char* name) const
{
	return _attributes.HasNamedAttribute(name);
}

const cffAttribute& cffNode::GetNamedAttribute(const char* name) const
{
	return _attributes.GetNamedAttribute(name);
}

const cffAttribute& cffNode::GetAttribute (unsigned idx) const
{
	return _attributes.GetAttribute(idx);
}


void cffNode::Debug(int indent)
{
	PrintIndent (indent);
	printf ("%s ", _name.c_str ());
	_attributes.Debug ();
	printf ("\n");
	std::list<cffNode*>::iterator it;
	for (it = _nodes.begin(); it != _nodes.end(); ++it)
		{
			cffNode* node = *it;
			node->Debug(indent+1);
		}

}

void cffNode::AddNode(cffNode *node)
{
	if (node)
		{
			node->AddRef ();
			node->_parent = this;
			_nodes.push_back(node);
		}
}

void cffNode::PrintIndent(int indent)
{
	for (; indent>0; --indent)
		{
			printf ("  ");
		}
}

cffParser::cffParser()
{
}
;

ceRef<cffNode> cffParser::Parse (const char* filename)
{
	global_ast_node = new cffNode ();
	global_ast_node->SetName ("root");
	
	yylineno = 0;
	yyin = fopen (filename, "rt");
	int error = yyparse ();
	if (error)
		{
			global_ast_node->Release();
			global_ast_node = 0;
		}

	return global_ast_node;
}


struct yy_buffer_state;
typedef yy_buffer_state *YY_BUFFER_STATE;
extern int yyparse();
extern YY_BUFFER_STATE yy_scan_string(const char *);
extern void yy_delete_buffer( YY_BUFFER_STATE b );
extern void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer );


ceRef<cffNode> cffParser::ParseFromBuffer(const char *buffer)
{
	// create a new buffer an copy the data with the correction
	// of binary/text read format
	size_t size = strlen(buffer);
	char* nb = new char [size + 1];
	char *nbp = nb;
	for (size_t i=0; i<size; ++i)
		{
			if (buffer[i] != '\n' && buffer[i] != '\r')
				{
					*nbp++ = buffer[i];
				}
			else
				{
					if (i < (size-1))
						{
							char p0 = buffer[i];
							char p1 = buffer[i+1];
							if (p0 == '\n')
								{
									*nbp++ = '\n';
									if (p1 == '\r')
										{
											++i;
										}
								}
							else if (p0 == '\r')
								{
									*nbp++ = '\n';
									if (p1 == '\n')
										{
											++i;
										}
								}
						}
				}
		}
	*nbp++ = '\0';

	global_ast_node = new cffNode ();
	global_ast_node->SetName ("root");

	YY_BUFFER_STATE bs = yy_scan_string(nb);
	yy_switch_to_buffer (bs);
	yylineno = 0;
	int error = yyparse ();
	yy_delete_buffer(bs);
  delete [] nb;

	if (error)
		{
			global_ast_node->Release();
			global_ast_node = 0;
		}
	return global_ast_node;
}
